
package com.feib.ebilling.job.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import javax.annotation.Resource;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;


/**
 * @title (#)MqService.java<br>
 * @description <br>
 * @author Jimmy Liu<br>
 * @version 1.0.0 2010/11/25
 * @copyright Far Eastern International Bank Copyright (c) 2010<br>
 * @2010/12/10 create by Jimmy Liu<br>
 */
@Service("mqService")
@Scope(value=BeanDefinition.SCOPE_PROTOTYPE)
public class MqService extends BaseService implements InitializingBean {

    public static final String DEFAULT_MQ_HOST_NAME = "CSF_NONEXML";

    /** MQ RF 訊息格式設定的常數，Class MQMessage所用 */
    public static final String MQFMT_RF_HEADER_2 = "MQHRF2  ";

    /** MQ RF 訊息內容的Header 設定 */
    public static final byte[] MQRFH_STRUC_ID = { 'R', 'F', 'H', ' ' };

    /** MQ RF 訊息內容的Header 設定 */
    public static final int MQRFH_VERSION_2 = 2;

    /** MQ RF 訊息內容的Header 設定 */
    public static final int MQRFH_STRUC_LENGTH_FIXED_2 = 40;

    /** MQ RF 訊息內容的Header 設定 */
    public static final byte[] MQFMT_STRING_ARRAY = { 'M', 'Q', 'S', 'T', 'R', ' ', ' ', ' ' };

    /** MQ RF 訊息內容的Header 設定 */
    public static final int MQRFH_NONE = 0x00000000;



    //public static Properties prop = new Properties();

    /** 電文收送型態--XML格式 */
    public static char MSG_TYPE_XML = 'X';

    /** 電文收送型態--RHF2格式 */
    public static char MSG_TYPE_NONEXML = 'R';

    /** MQ 主機名稱 或 ip */
    private static String MQhostname;

    /** MQ 主機TCP PORT */
    private static int MQport;

    /** MQ 連線連線狀態 0:使用折返回訊息 1:訊息送MQ主機 */
    public static String MQonline;

    /** MQ 連線至後端主機資訊 */
    private static HashMap<String, String[]> HostInfo;

    /** MQ 折返TOTA訊息pool */
    private static HashMap<String, List<String>> FAKE_TOTA_POOL;

    /** 初始化設定值狀態 */
    private static boolean isOK_Info = false;

    /** 設定檔工作目錄 */
    private static String MQ_WORK_HOME;

    private List<String> myFakeTota;

    private int myFakeIdx, myExprityDay, myWaitIntervalDay;

    private String myMQmanager, myMQchannel, myRequestQueue, myResponseQueue, myRFHeader; 
    private String myTITA;

    private String myMessageID;

    @Resource(name="mqProperties")
    private Properties mqProperties;


    /**
     * 初始化 SendMQ 設定
     * @return done if true, else fail.
     */
    public boolean init()
    {
        if (isOK_Info == true)
            return true;

        try {
            logger.info("Init MQ configure starting...");
            MQ_WORK_HOME = mqProperties.getProperty("MQ_WORK_HOME");
            logger.info("MQWORKHOME={}", MQ_WORK_HOME);
            MQhostname = mqProperties.getProperty("MQhostname");
            logger.info("MQhostname={}", MQhostname);
            MQport = Integer.parseInt(mqProperties.getProperty("MQport"));
            logger.info("MQport=" + MQport);
            MQonline = mqProperties.getProperty("MQonline");
            logger.info("MQonline={}", MQonline);

            HostInfo = new HashMap<String, String[]>();
            int idx = 1;

            while (true)
            {
                String[] tmp = new String[10];
                tmp[0] = mqProperties.getProperty(idx + "#Host");               
                tmp[1] = mqProperties.getProperty(idx + "#MQmanager");
                tmp[2] = mqProperties.getProperty(idx + "#MQchannel");
                tmp[3] = mqProperties.getProperty(idx + "#RequestQueue");
                tmp[4] = mqProperties.getProperty(idx + "#ResponseQueue");
                tmp[5] = mqProperties.getProperty(idx + "#ExprityDay");
                tmp[6] = mqProperties.getProperty(idx + "#WaitIntervalDay");
                /* 全長要是2、4的能整除 */
                tmp[7] = mqProperties.getProperty(idx + "#RFHeader");
                tmp[8] = mqProperties.getProperty(idx + "#MessageID");
                tmp[9] = mqProperties.getProperty(idx + "#TITA");

                if (tmp[0] != null){
                    logger.info(idx + "#Host={}", tmp[0]);
                    logger.info(idx + "#MQmanager={}", tmp[1]);
                    logger.info(idx + "#MQchannel={}", tmp[2]);
                    logger.info(idx + "#RequestQueue={}", tmp[3]);
                    logger.info(idx + "#ResponseQueue={}", tmp[4]);
                    logger.info(idx + "#ExprityDay={}", tmp[5]);
                    logger.info(idx + "#WaitIntervalDay={}", tmp[6]);
                    /* 全長要是2、4的能整除 */
                    logger.info(idx + "#RFHeader={}", tmp[7]);
                    logger.info(idx + "#MessageID={}", tmp[8]);
                    logger.info(idx + "#TITA={}", tmp[9]);
                }

                int cntAllNull = 0;
                int cntPartNull = 0;
                for (int i = 0; i < tmp.length; i++)
                {
                    if (tmp[i] == null)
                        cntAllNull += 1;
                    if (tmp[i] == null || tmp[i].length() == 0)
                        cntPartNull += 1;
                }

                if (cntAllNull == tmp.length)
                {
                    idx++;
                    break;
                }

                if (cntPartNull > 1)
                {
                    idx++;
                    continue;
                }

                try
                {
                    Integer.parseInt(tmp[5]);
                    Integer.parseInt(tmp[6]);
                }
                catch (Exception ex)
                {
                    idx++;
                    continue;
                }

                HostInfo.put(tmp[0], tmp);
                idx++;
            }



            // 讀取折返電文檔(TOTA)資料
            FAKE_TOTA_POOL = new HashMap<String, List<String>>();
            File xfile = new File(MQ_WORK_HOME + "/totapool");
            List<String> totaData;
            if (xfile.exists() && xfile.isDirectory())
            {
                String[] key = xfile.list();
                for (int i = 0; i < key.length; i++)
                {
                    xfile = new File(MQ_WORK_HOME + "/totapool/" + key[i]);
                    if (!xfile.isFile())
                        continue;

                    totaData = new ArrayList<String>();

                    String tempStr = null;
                    BufferedReader in2 = null;
                    try {
                        in2 = new BufferedReader(new FileReader(xfile.getAbsoluteFile()));
                        while ((tempStr = in2.readLine()) != null)
                        {               
                            if (logger.isDebugEnabled())
                                logger.debug("{}=[{}]", key[i], tempStr);
                            totaData.add(tempStr);
                        }               
                        FAKE_TOTA_POOL.put(key[i], totaData);

                    }
                    finally
                    {
                        try {
                            if (null != in2)
                                in2.close();
                        }
                        catch (Exception e2) {
                            // do nothing.
                        }
                    }
                }
            }

            isOK_Info = true;
            logger.info("Init MQ configure end.");
        }
        catch (Exception e) {
            logger.error("MQ 起始錯誤！", e);
            isOK_Info = false;
        }

        return isOK_Info;
    }

    /**
     * 
     * @param host
     * @throws Exception
     */
    public void setMyHostInfo(String host) throws Exception
    {
        String[] myHostInfo = (String[]) HostInfo.get(host);

        if (myHostInfo == null)
            throw new IllegalArgumentException("不合法的 host");

        this.myMQmanager = myHostInfo[1];
        this.myMQchannel = myHostInfo[2];
        this.myRequestQueue = myHostInfo[3];
        this.myResponseQueue = myHostInfo[4];
        this.myExprityDay = Integer.parseInt(myHostInfo[5]);
        this.myWaitIntervalDay = Integer.parseInt(myHostInfo[6]);
        this.myRFHeader = myHostInfo[7];
        this.myMessageID = myHostInfo[8];
        this.myTITA = myHostInfo[9];

        MQEnvironment.hostname = MQhostname;
        MQEnvironment.port = MQport;
        MQEnvironment.channel = myMQchannel;
        MQEnvironment.CCSID = 950;
        MQEnvironment.userID = "";
        MQEnvironment.password = "";

    }

    /**
     * 將電文字串傳送至後端主機
     * @param tita
     * @param msgType
     * @param msgId
     * @param msgName
     * @throws Exception
     */
    public void send(String tita, char msgType, String msgId, String msgName) throws Exception
    {
        if (!init())
            return ;

        MQQueueManager qManager = null;
        MQQueue requestQueue = null;

        try
        {
            if (!(msgType == MSG_TYPE_XML || msgType == MSG_TYPE_NONEXML))
                throw new IllegalArgumentException("不合法的 msgType");

            if (!isDigit(msgId) || msgId.length() != 10)
                throw new IllegalArgumentException("不合法的 msgId");

            msgId = msgId + "              ";

            logger.info("#MSGID={}", msgId);
            logger.info("#TITA={}", tita);

            if ("0".equals(MQonline))
            {
                this.myFakeIdx = 0;
                myFakeTota = FAKE_TOTA_POOL.get(msgName);
                return;
            }
            else if (msgType == MSG_TYPE_XML)
            {
                qManager = new MQQueueManager(myMQmanager);

                int openOptions = MQC.MQOO_OUTPUT;
                requestQueue = qManager.accessQueue(myRequestQueue, openOptions, null, null, null);

                MQMessage requestMsg = new MQMessage();
                requestMsg.characterSet = 950;
                requestMsg.messageId = msgId.getBytes();
                requestMsg.writeString(tita);
                requestMsg.expiry = myExprityDay;

                MQPutMessageOptions requestPmo = new MQPutMessageOptions();

                requestQueue.put(requestMsg, requestPmo);
            }
            else
            {
                qManager = new MQQueueManager(myMQmanager);

                int req_openOptions = MQC.MQOO_OUTPUT | MQC.MQOO_BIND_NOT_FIXED;
                requestQueue = qManager.accessQueue(myRequestQueue, req_openOptions, null, null, null);

                MQMessage requestMsg = new MQMessage();
                requestMsg.characterSet = 950;
                requestMsg.format = MQFMT_RF_HEADER_2;
                requestMsg.messageId = msgId.getBytes();
                requestMsg.replyToQueueName = myRequestQueue;
                requestMsg.replyToQueueManagerName = myMQmanager;
                requestMsg.persistence = MQC.MQPER_PERSISTENT;
                requestMsg.expiry = myExprityDay;
                /** **************************************************************** */
                /* Imbed an RF Header into the requestMsg stream */
                /** **************************************************************** */
                requestMsg.write(MQRFH_STRUC_ID); // StructId
                requestMsg.writeInt(MQRFH_VERSION_2); // RFH Version
                requestMsg.writeInt(MQRFH_STRUC_LENGTH_FIXED_2 + myRFHeader.length());
                requestMsg.writeInt(MQC.MQENC_NATIVE); // Encoding
                requestMsg.writeInt(MQC.MQCCSI_INHERIT); // qmgr CCSID
                requestMsg.write(MQFMT_STRING_ARRAY); // Format
                requestMsg.writeInt(MQRFH_NONE); // Flags
                requestMsg.writeInt(1208); // NameValueCCSID
                requestMsg.writeInt(myRFHeader.length()); // NameValueLength
                /** **************************************************************** */
                /* Append the name/values pairs to the message stream */
                /** **************************************************************** */
                requestMsg.writeBytes(myRFHeader);
                requestMsg.writeString(tita);

                MQPutMessageOptions requestPmo = new MQPutMessageOptions();

                requestQueue.put(requestMsg, requestPmo);
            }      

        }
        catch (MQException ex)
        {
            logger.error(ex.toString(), ex);
            throw ex;
        }
        catch (Exception ex)
        {
            logger.error(ex.toString(), ex);
            throw ex;
        }
        finally
        {
            if (requestQueue != null && requestQueue.isOpen())
                requestQueue.close();
            if (qManager != null && qManager.isConnected())
                qManager.disconnect();
        }

    }

    /**
     * 收取主機回覆的電文字串
     * @param msgId
     * @return
     * @throws Exception
     */
    public String rcv(String msgId) throws Exception
    {
        MQQueueManager qManager = null;
        MQQueue responseQueue = null;

        String totaStr = null;
        try
        {
            if (!isDigit(msgId) || msgId.length() != 10)
                throw new IllegalArgumentException("不合法的 msgId");

            msgId = msgId + "              ";

            if ("0".equals(MQonline))
            {       
                if (myFakeIdx > (myFakeTota.size() - 1))
                    return null;
                String tmpStr = (String) myFakeTota.get(myFakeIdx);             
                myFakeIdx++;            
                totaStr = tmpStr;
                logger.info(" #TOTA={}", totaStr);
                return totaStr;
            }

            qManager = new MQQueueManager(myMQmanager);

            int openOptions = MQC.MQOO_INPUT_AS_Q_DEF;
            responseQueue = qManager.accessQueue(myResponseQueue, openOptions, null, null, null);

            MQMessage responseMsg = new MQMessage();
            responseMsg.characterSet = 950;
            responseMsg.format = MQC.MQFMT_STRING;
            responseMsg.messageId = msgId.getBytes();

            MQGetMessageOptions responseGmo = new MQGetMessageOptions();
            responseGmo.options = MQC.MQGMO_WAIT;
            responseGmo.waitInterval = myWaitIntervalDay;

            responseQueue.get(responseMsg, responseGmo);

            byte[] totabyte = new byte[responseMsg.getMessageLength()];
            responseMsg.readFully(totabyte, 0, responseMsg.getMessageLength());
            totaStr = new String(totabyte);

            logger.info(" #MSGID={}", msgId); 
            logger.info(" #TOTA={}", totaStr);
        }
        catch (MQException ex)
        {
            logger.error(ex.toString(), ex);
            throw ex;
        }
        catch (Exception ex)
        {
            logger.error(ex.toString(), ex);
            throw ex;
        }
        finally
        {
            if (!"0".equals(MQonline))
            {   
                if (responseQueue != null && responseQueue.isOpen())
                    responseQueue.close();
                if (qManager.isConnected())
                    qManager.disconnect();
            }
        }
        return totaStr;
    }

    private boolean isDigit(String str) throws Exception
    {
        try
        {
            for (int i = 0; i < str.length(); i++)
            {
                if (!Character.isDigit(str.charAt(i)))
                    throw new NumberFormatException();
            }
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    /* (non-Javadoc)
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        init();
        setMyHostInfo(DEFAULT_MQ_HOST_NAME);        
    }
    
    /**
     * @return the myTITA
     */
    public String getMyTITA() {
        return myTITA;
    }

    
    /**
     * @param myTITA the myTITA to set
     */
    public void setMyTITA(String myTITA) {
        this.myTITA = myTITA;
    }

    
    /**
     * @return the myMessageID
     */
    public String getMyMessageID() {
        return myMessageID;
    }

    
    /**
     * @param myMessageID the myMessageID to set
     */
    public void setMyMessageID(String myMessageID) {
        this.myMessageID = myMessageID;
    }
}
